/**   
  * @Title: CrmLogAspect.java 
  * @Package com.cyber.framework.log 
  * @Description: TODO(用一句话描述该文件做什么) 
  * @author cssuger@163.com   
  * @date 2018年3月5日 下午5:24:25 
  * @version V1.0   
 */
package com.bicon.base.data.common.log;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bicon.base.data.common.Constants;
import com.bicon.base.data.common.DbBuilder;
import com.bicon.base.data.common.StringTools;
import com.bicon.base.data.common.UUIDGenerator;
import com.bicon.base.data.common.exception.BusinessException;
import com.bicon.base.data.common.log.annotation.LogAnnotation;
import com.bicon.base.data.common.tools.DateUtil;
import com.bicon.base.data.common.tools.JsonUtil;
import com.bicon.base.data.common.tools.MessageTip;
import com.bicon.base.data.common.vo.Result;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

/** 
  *@ClassName: CrmLogAspect 
  *@Description: TODO(这里用一句话描述这个类的作用) 
  *@author cssuger@163.com 
  *@date 2018年3月5日 下午5:24:25   
*/
@Aspect
public class CrmLogAspect {

	private Logger logger = LoggerFactory.getLogger(CrmLogAspect.class);
	
	/**
	 * 线程池
	 */
	protected final static ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5, 15, 30L,
			TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(1024), new ThreadPoolExecutor.DiscardPolicy()) {
	};

	private static String path;

	private final static UUIDGenerator uuidgenerator = new UUIDGenerator();
	
	private DbBuilder dbBuilder;

//	@Autowired
//	private BaseTemplete baseMongodbTemplete;
	
	
	
	public DbBuilder getDbBuilder() {
		return dbBuilder;
	}
	public void setDbBuilder(DbBuilder dbBuilder) {
		this.dbBuilder = dbBuilder;
	}

	private final static Map<String, String> MAP = Maps.newHashMap();
	static {
		MAP.put("igservice", "综合业务系统");
		MAP.put("sys", "权限管理");
		MAP.put("cp", "清结算");
		MAP.put("integratedmanagement", "榆林综合业务系统");
	}

	/**
	 * 异步线程
	 */
	private final static ListeningExecutorService SERVICE = MoreExecutors.listeningDecorator(THREAD_POOL_EXECUTOR);

//	private static Properties properties = new Properties();

	

	private ClassPool pool = ClassPool.getDefault();

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		CrmLogAspect.path = path;
	}

	public CrmLogAspect() {
	}

//	private static void loadProperties() {
//		properties = new PropertiesUtil(path, false).getProperties();
//	}

	private  class LogTask implements Callable<String> {
		
//		private Logger logger = LoggerFactory.getLogger(LogTask.class);

		private String systemName;
		private String moduleName;
		private String operateType;
		private String spendTime;
		private String serviceClass;
		private String opTime;// 操作时间
		private String tip;
		private String methodName;
		private String reason;
		private String operator;//操作人
		private String ip;//操作人登录IP
		private String mparams;//方法参数
		private String desc;//方法描述
		

		public LogTask(String systemName, String moduleName, String operateType, String spendTime, String serviceClass,
				String opTime, String tip, String methodName, String reason,String operator,String ip,String params,String desc) {
			this.systemName = systemName;
			this.moduleName = moduleName;
			this.operateType = operateType;
			this.spendTime = spendTime;
			this.serviceClass = serviceClass;
			this.opTime = opTime;
			this.tip = tip;
			this.methodName = methodName;
			this.reason = reason;
			this.operator = operator;
			this.ip = ip;
			this.mparams = params;
			this.desc = desc;
		}

		@Override
		public String call() throws Exception {
			
			// 数据库操作
//			String sql = "INSERT INTO SYS_LOG(ID, SYSNAME ,MODELNAME ,OPERATETYPE, SPENDTIME,SERVICECLASS,OPTIME, METHODNAME, TIP, REASON,OPERATOR,IP,PARAMS,METHOD_DESC) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
//			Object[] params = { uuidgenerator.generate().toString(), systemName, moduleName, operateType, spendTime,
//					serviceClass, opTime, methodName, tip, reason,operator,ip,mparams,desc };
//			System.err.println("保存日志：sql:{},参数:{}"+sql+"------------"+JsonUtil.objectToJson(params));
//			int res = DbBuilder.instance().save(sql, params);
			LogEntity entity = new LogEntity();
			entity.setModuleName(moduleName);
			entity.setOperateType(operateType);
			entity.setOpTime(opTime);
			entity.setOperator(operator);
			entity.setServiceClass(serviceClass);
			entity.setSpendTime(spendTime);
			entity.setSystemName(systemName);
			entity.setTip(tip);
			entity.setMethodName(methodName);
			entity.setReason(reason);
			entity.setParams(mparams);
			entity.set_id(uuidgenerator.generate().toString());
			entity.setIp(CrmLogAspect.getLinuxLocalIp());
			//return res == 1 ? "日志入库成功" : "日志入库失败";
			String res = "0";
			try {
				//baseMongodbTemplete.save(entity);
				res = "1";
			}catch(Exception ex) {
				logger.error("插入mongodb错误,错误信息为:{}", ex.fillInStackTrace());
				res = "0";
			}
			return res ;
		}

	}

	 /**
     * 获取本地Host名称
     */
    public static String getLocalHostName() throws UnknownHostException {
        return InetAddress.getLocalHost().getHostName();
    }
	
	
	/**
     * 获取Linux下的IP地址
     *
     * @return IP地址
     * @throws SocketException
     */
    private static String getLinuxLocalIp() throws SocketException {
        String ip = "";
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                String name = intf.getName();
                if (!name.contains("docker") && !name.contains("lo")) {
                    for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                        InetAddress inetAddress = enumIpAddr.nextElement();
                        if (!inetAddress.isLoopbackAddress()) {
                            String ipaddress = inetAddress.getHostAddress().toString();
                            if (!ipaddress.contains("::") && !ipaddress.contains("0:0:") && !ipaddress.contains("fe80")) {
                                ip = ipaddress;
                               // System.out.println(ipaddress);
                            }
                        }
                    }
                }
            }
        } catch (SocketException ex) {
            System.out.println("获取ip地址异常");
            ip = "127.0.0.1";
            ex.printStackTrace();
        }
        //System.out.println("IP:"+ip);
        return ip;
    }
	
	/**
	 * 切面的前置方法 即方法执行前拦截到的方法 记录并输出 在目标方法执行之前的通知
	 * 
	 * @param joinPoint
	 */
	public void doBefore(JoinPoint joinPoint) {
		// System.out.println("======================方法开始======================");
		// Object object = joinPoint.getSignature();
		// String methodName = joinPoint.getSignature().getName();
		// List<Object> list = Arrays.asList(joinPoint.getArgs());
		// Date date = new Date();
		// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		// String rightnow=sdf.format(date);
		// System.out.println(rightnow+"执行了【"+object+"方法开始执行......】");
		// System.out.println("******参数"+list+"******");
	}

	/**
	 * 切面的后置方法，不管抛不抛异常都会走此方法 在目标方法执行之后的通知
	 * 
	 * private static final long serialVersionUID = -5854872510644116447L;
	 * 
	 * private Date opTime;//操作时间 private String className;//类名 private String
	 * moduleName;//模块名 private String opMark;//标志位 private String
	 * msg;//记录内容+当前操作参数如：订单号，协议号 private SystemType systemType;//源系统 private
	 * BusinessType businessType;//业务类型(日常操作类、业务类) private String operator;//操作者
	 * private String ip;//操作者ip地址 private LogType logType;//日志类型 private Long
	 * spendTime;//方法耗时 private String errorInfo;//错误信息
	 * 
	 * @param joinPoint
	 */
	public void doAfter(JoinPoint joinPoint) {
		// MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
		// Method method = methodSignature.getMethod();
		// System.out.println("执行了【方法结束......】");
	}

	/**
	 * 在方法正常执行通过之后执行的通知叫做返回通知 可以返回到方法的返回值 在注解后加入returning
	 * 
	 * @param joinPoint
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 */
	public void afterReturn(ProceedingJoinPoint proceedingJoinPoint, Object result) throws NoSuchMethodException, SecurityException {

	}

	/**
	 * 在目标方法非正常执行完成 发生异常 抛出异常的时候会走此方法 获得异常可以用throwing
	 * 
	 * @param joinPoint
	 * @param ex
	 */
	public void afterThrowing(JoinPoint joinPoint, Exception ex) {
		// Object object = joinPoint.getSignature();
		// Date date = new Date();
		// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		// String rightnow=sdf.format(date);
		// System.out.println(rightnow+"执行了【"+object+"方法发生异常......】"+"【异常报告:"+ex+"】");
		// System.out.println("xxxxxxxxxxxxxxxxxx方法发生异常结束xxxxxxxxxxxxxxxxxx");
	}

	/**
	 * 环绕通知需要携带ProceedingJoinPoint 这个类型的参数 环绕通知类似于动态代理的全过程
	 * ProceedingJoinPoint类型的参数可以决定是否执行目标函数 环绕通知必须有返回值 private static final long
	 * serialVersionUID = -5854872510644116447L;
	 * 
	 * private Date opTime;//操作时间 private String className;//类名 private String
	 * moduleName;//模块名 private String opMark;//标志位 private String
	 * msg;//记录内容+当前操作参数如：订单号，协议号 private SystemType systemType;//源系统 private
	 * BusinessType businessType;//业务类型(日常操作类、业务类) private String operator;//操作者
	 * private String ip;//操作者ip地址 private LogType logType;//日志类型 private Long
	 * spendTime;//方法耗时 private String errorInfo;//错误信息
	 * 
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable 
	 */
	@SuppressWarnings("rawtypes")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		// 获取执行的方法名称
		String methodName = proceedingJoinPoint.getSignature().getName();
		Class<? extends Object> clazz = proceedingJoinPoint.getTarget().getClass();
		Method method = proceedingJoinPoint.getTarget().getClass().getMethod(methodName,
				getClassType(methodName, clazz));
		// 获取参数值
		Object[] args = proceedingJoinPoint.getArgs();
		String argstring = "";
		if(null != args && args.length > 0) {
			//方法参数
			for(Object o : args) {
				argstring += JsonUtil.objectToJson(o)+",";
			}
			//如果参数太长，则截取前1000字符，整个参数打印到控制台
			if(argstring.length() > 1000) {
				logger.info("参数太长，类:"+clazz.getName()+",方法:"+methodName+",参数:"+argstring);
				argstring = "long params:"+argstring.substring(0, 1000);
			}
			//System.err.println("方法参数："+argstring);
		}
		//用户名和地址
		//HttpServletRequest request = SysContent.getRequest();
		String userName = "system";
		String remoteAddr = "";
//		if(null != request) {
//			HttpSession session = request.getSession();
//			if(null != session) {
//				String sn = (String) session.getAttribute("suserName");
//				if(StringUtils.isNotBlank(sn)) userName = sn;
//			}
//			remoteAddr = request.getRemoteAddr();
////			System.err.println("用户地址:"+userName+"---"+remoteAddr);
//		}
		
		
		LogAnnotation logAnnation = method.getAnnotation(LogAnnotation.class);
		String tip = "";
		String className = clazz.getName();
		Object returnMsg = null;
		long now = System.currentTimeMillis();
		String systemName = null;
		String moduleName = null;
		String operateType = null;
		String desc = null;
		if (null != logAnnation) {
			
		// 执行目标方法
		systemName = logAnnation.sysName();// 系统名称
		moduleName = logAnnation.model();// 模块名称
		operateType = logAnnation.operatetype();// 操作类型
//		if(StringUtils.isBlank(operateType)) {
//			if (methodName.contains("insert") || methodName.contains("add")) {
//				operateType = Constants.INSERT_TYPE;
//			}
//			if (methodName.contains("delete") || methodName.contains("remove")) {
//				operateType = Constants.DELETE_TYPE;
//			}
//			if (methodName.contains("update") || methodName.contains("edit")) {
//				operateType = Constants.UPDATE_TYPE;
//			}
//			if (methodName.contains("page") || methodName.contains("query") || methodName.contains("find")
//					|| methodName.contains("get")) {
//				operateType = Constants.SELECT_TYPE;
//			}
//		}
		desc = logAnnation.desc();//方法描述

		 } else {
			if (logger.isDebugEnabled()) {
				logger.debug("该方法没有添加日志注解");
			}
			// 执行目标方法
				
		}
		
		tip = "1";
		try {
			returnMsg = proceedingJoinPoint.proceed();
			if(returnMsg instanceof Boolean) {
				Boolean flage = (Boolean)returnMsg;
				if( methodName.contains("insert") ||  methodName.contains("save") ||  methodName.contains("add") || methodName.contains("delete") || methodName.contains("remove") || methodName.contains("update") || methodName.contains("edit")) {
					if(!flage) {
						throw new BusinessException(Constants.ERROR_STATE, "系统异常!");
					}
				}
				
			}
			if(returnMsg instanceof String) {
				String flage = (String)returnMsg;
				if( methodName.contains("save") || methodName.contains("insert") || methodName.contains("add") || methodName.contains("delete") || methodName.contains("remove") || methodName.contains("update") || methodName.contains("edit")) {
					if(!StringTools.isBlank(flage) && StringTools.mkString(flage).contains(Constants.ERROR_FLAGE)) {
						logger.error("发生异常，异常信息:{}",flage);
						throw new BusinessException(Constants.ERROR_STATE, flage);
					}
				}
				
			}
			if(returnMsg instanceof Integer) {
				Integer flage = (Integer)returnMsg;
				if(methodName.contains("save") || methodName.contains("insert") || methodName.contains("add") || methodName.contains("delete") || methodName.contains("remove") || methodName.contains("update") || methodName.contains("edit")) {
					if(flage <=0) {
						logger.error("发生异常，异常信息:{}",flage);
						throw new BusinessException(Constants.ERROR_STATE, "发送异常!");
					}
				}
				
			}
			
			if(returnMsg instanceof MessageTip) {
				MessageTip flage = (MessageTip)returnMsg;
				if("false".equals(flage.getState()) ) {
					if(!Constants.NO_EXCEPTION_CODE.equals(flage.getCode())){
						logger.error("发生异常，异常信息:{}",flage);
						throw new BusinessException(Constants.ERROR_STATE, flage.getTip());	
					}
					
				}
			}
			if(returnMsg instanceof Result) {
				Result result = (Result)returnMsg;
				if(!result.getOpResutl()) {
					//等于0001不抛异常
					if(!Constants.NO_EXCEPTION_CODE.equals(result.getCode())){
						logger.error("发生异常，异常信息:{}",result.getMessage());
						throw new BusinessException(Constants.ERROR_STATE, result.getMessage());	
					}
					//throw new BusinessException(Constants.ERROR_STATE, flage.getMessage());
				}
			}
			long spendTime = System.currentTimeMillis() - now;
			logger.info("花费时间:" + spendTime + "ms");
			SERVICE.submit(new LogTask(systemName, moduleName, operateType, spendTime + "", className,
					DateUtil.now("yyyy-MM-dd HH:mm:ss"), tip, methodName, "",userName,remoteAddr,argstring,desc));
		}catch(Throwable e) {
			//e.printStackTrace();
			tip = "0";
			String resaon = Throwables.getStackTraceAsString(e);
			long spendTime = System.currentTimeMillis() - now;
			logger.info("花费时间:" + spendTime + "ms");
			//logger.error("发生异常，异常信息:{}",ExceptionUtils.getMessage(e));
			SERVICE.submit(new LogTask(systemName, moduleName, operateType, spendTime + "", className,
					DateUtil.now("yyyy-MM-dd HH:mm:ss"), tip, methodName, resaon,userName,remoteAddr,argstring,desc));
			throw e;
		}
		
		// stopwatch.stop();
		return returnMsg;
	}

	private String builderPrarms(Object[] args) {
		StringBuffer prarmSb = new StringBuffer();
		if (null != args) {
			for (int i = 0; i < args.length; i++) {
				prarmSb.append(args[i]).append(",");
			}
		}
		return prarmSb.toString();
	}

	/**
	 * 获取所有参数类型
	 * @param methodName
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private Class[] getClassType(String methodName, Class clazz) {
		ClassClassPath classPath = new ClassClassPath(clazz);
		pool.insertClassPath(classPath);
		CtClass cc;
		try {
			cc = pool.get(clazz.getName());
			CtMethod ctMethod = cc.getDeclaredMethod(methodName);
			int paramSize = ctMethod.getParameterTypes().length;
			Class[] clses = new Class[paramSize];
			for (int i = 0; i < paramSize; i++) {
				clses[i] = Class.forName(ctMethod.getParameterTypes()[i].getName());
			}
			return clses;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;

	}
}
